


#include"mcu_PPP.h"







///////////  MODBUS CRC16校验算法：CRC16=X16+X15+X2+1///////////////
unt8  CRC_TABLE_H[256] =		//High
{
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
	0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40
};
unt8  CRC_TABLE_L[256] =		//Low
{
	0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,0x07,0xC7,0x05,0xC5,0xC4,0x04,
	0xCC,0x0C,0x0D,0xCD,0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,0x08,0xC8,
	0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,
	0x14,0xD4,0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,0x11,0xD1,0xD0,0x10,
	0xF0,0x30,0x31,0xF1,0x33,0xF3,0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
	0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A,0x3B,0xFB,0x39,0xF9,0xF8,0x38,
	0x28,0xE8,0xE9,0x29,0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,0xEC,0x2C,
	0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,
	0xA0,0x60,0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,0xA5,0x65,0x64,0xA4,
	0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,0x6E,0xAE,0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,
	0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,0xBE,0x7E,0x7F,0xBF,0x7D,0xBD,0xBC,0x7C,
	0xB4,0x74,0x75,0xB5,0x77,0xB7,0xB6,0x76,0x72,0xB2,0xB3,0x73,0xB1,0x71,0x70,0xB0,
	0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
	0x9C,0x5C,0x5D,0x9D,0x5F,0x9F,0x9E,0x5E,0x5A,0x9A,0x9B,0x5B,0x99,0x59,0x58,0x98,
	0x88,0x48,0x49,0x89,0x4B,0x8B,0x8A,0x4A,0x4E,0x8E,0x8F,0x4F,0x8D,0x4D,0x4C,0x8C,
	0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40
};

/// <summary>
/// 计算CRC校验码。
/// 发送端校验方法：设发送端发送n个字符，先计算2字节CRC校验码放到数据后面。(CRC%256)在n+1，(CRC/256)在n+2。
/// 接收端校验方法：接收端计算n+2个字符的校验码，如果校验码为0，说明校验通过，否则不通过。
/// </summary>
/// <param name="p">数据指针</param>
/// <param name="n">数据长度</param>
/// <returns>校验码</returns>
unt16 MODBUS_CRC16(unt8* p, int n)
{
	unt8 	i;
	unt16	crc16 = 0xffff;		//预置16位CRC寄存器为0xffff（即全为1）
	do
	{
		i = *p ^ (unt8)crc16;
		crc16 = ((unt8)(crc16 >> 8) ^ CRC_TABLE_H[i]) | ((unt16)CRC_TABLE_L[i] << 8);
		p++;
	} while (--n != 0);
	return	(crc16);
}


/*
发送指令流程：
1、先计算字符串数据的CRC16校验码，并添加到字符串末尾。
2、进行字节填充（校验码可能出现0x7E的字节）。（定义转义字符为0x7D）
	1）信息字段中出现0x7E，则转换成0x7D，0x5E。
	2）信息字段中出现0x7D，则转换成0x7D，0x5D。
	3）信息字段中出现控制字符（数值小于0x20），则在该字符前加入一个0x7D字节，同时将该字符加0x20.
		比如：0x03 转换成0x7D,0x23.（大于0x20的ASCII都是可显示的字符。
		其中，值大于128的字符是扩展ASCII字符，包括特殊符号字符、外来语字母和图形符号）
3、给数据加上帧头帧尾0x7E，发送数据。
所以，只要出现0x7E，就是帧的分隔符。
*/
/// <summary>
/// 生成PPP协议的字符串.
/// </summary>
/// <param name="indata">原始数据</param>
/// <param name="indata_len">原始数据长度</param>
/// <param name="str">转化后的字符串</param>
/// <param name="str_len">转化后的字符串最大长度（缓存数组大小）</param>
/// <returns>转化后的字符串的实际长度。需要确保实际长度小于 （缓存数组最大长度-3）。</returns>
int PPP_create_string(unt8* indata, int indata_len, unt8* str, int str_len)
{
	int str_index = 0;
	int data_index = 0;
	str[str_index] = 0x7e;
	str_index++;
	unt16 crc = MODBUS_CRC16(indata, indata_len);
	unt8 c[2];
	c[0] = crc % 256;
	c[1] = crc / 256;

	for (data_index = 0; data_index < indata_len && str_index < str_len - 3; data_index++) {
		if (indata[data_index] == 0x7E) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = 0x5E;
			str_index++;
		}
		else if (indata[data_index] == 0x7D) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = 0x5D;
			str_index++;
		}
		else if (indata[data_index] < 0x20) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = indata[data_index] + 0x20;
			str_index++;
		}
		else {
			str[str_index] = indata[data_index];
			str_index++;
		}
	}
	//加上校验码
	for (data_index = 0; data_index < 2 && str_index < str_len - 3; data_index++) {
		if (c[data_index] == 0x7E) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = 0x5E;
			str_index++;
		}
		else if (c[data_index] == 0x7D) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = 0x5D;
			str_index++;
		}
		else if (c[data_index] < 0x20) {
			//进行转义
			str[str_index] = 0x7D;
			str_index++;
			str[str_index] = c[data_index] + 0x20;
			str_index++;
		}
		else {
			str[str_index] = c[data_index];
			str_index++;
		}
	}
	//加上帧尾
	str[str_index] = 0x7e;
	str_index++;
	return str_index;
}


/*
解析指令流程：
1、先去掉帧头帧尾，逆字符填充，得到干净的数据。
2、进行CRC校验。如果校验通过，则直接调用处理函数。否则舍弃。
*/



/// <summary>
/// 解析PPP协议
/// </summary>
/// <param name="ppp">PPP的结构体</param>
/// <param name="data">接收的一字节数据</param>
void PPP_receive_data(PPP* ppp, unt8 data)
{
	if (data == 0x7E) {
		if (ppp->receive_length != 0) {
			//之前有接收数据。先进行CRC校验。
			unt16 crc16 = MODBUS_CRC16((unt8*)ppp->receive_buf, ppp->receive_length);
			
			if (crc16 == 0) 
			{
				//校验通过，下面处理数据。
				ppp->receive_buf[ppp->receive_length] = 0;
				ppp->process(ppp->receive_buf, ppp->receive_length);
		//		printfs(" \r\n receive: %s", ppp->receive_buf);
			}
			else {
				//校验不通过，通知上位机。
			//	printfs(" \r\n CRC16 error ");
			}
		}
		ppp->receive_length = 0;
	}
	else if (data == 0x7D) { //转义字符。
		ppp->is_EscapeCharacter = true;
	}
	else  //其他字符
	{
		if (ppp->is_EscapeCharacter) { //是转义字符
			if (data < (0x20 + 0x20)) 	ppp->receive_buf[ppp->receive_length] = data - 0x20;
			else if (data == 0x5E) ppp->receive_buf[ppp->receive_length] = 0x7E;
			else if (data == 0x5D) ppp->receive_buf[ppp->receive_length] = 0x7D;
			else { //错误的转义字符。
			//	printfs(" \r\n EscapeCharacter error ");
			}
			ppp->is_EscapeCharacter = false;  //接收到转义字符后，重置转义标志。
		}
		else //普通字符。
		{
			ppp->receive_buf[ppp->receive_length] = data;
		}
		ppp->receive_length++;
		if (ppp->receive_length >= ppp_buf_capacity) { //帧的数据过长
			ppp->receive_length = 0;
		}
	}
}
//





void test_PPP()
{

	{
		//测试CRC校验
		unt16 crc;
		unsigned char buf[12] = "1234567890";

		crc = MODBUS_CRC16(buf, 5);
		buf[5] = crc % 256;
		buf[6] = crc / 256;
		printfs("\r\n test crc: %d", crc);

		crc = MODBUS_CRC16(buf, 7);
		printfs("\r\n test crc: %d", crc);
	}

	{
		unsigned char buf[256];
		unsigned char str[]="hello world";
		int j;
		int i = PPP_create_string((unt8*)str, 11, buf, 256);
		buf[i] = 0;
		printfs("%s", buf);

	//	PPP ppp;
		printfs(" \r\n next begin receive data \r\n ");
		for (j = 0; j < i; j++) {
		//	PPP_receive_data(&ppp, buf[j]);
		}

	}
}






